package main

import (
	"context"
	"fmt"
	"github.com/fsnotify/fsnotify"
	"github.com/gin-gonic/gin"
	"github.com/spf13/viper"
	"log"
	"okx_bot/config"
	"okx_bot/entity"
	"okx_bot/pkg/decimal"
	"okx_bot/pkg/generate"
	pkgokx "okx_bot/pkg/okx"
	"okx_bot/pkg/redisclient"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"
)

var (
	mutex sync.Mutex
	ctx   = context.Background()
)

// CreateOrder 创建交易订单
func CreateOrder(okx *pkgokx.Okx, redisClient *redisclient.RedisClient, price, ordType string, account *config.AccountConfig, strategy *config.AccountStrategy) error {
	// 构造Redis键
	key := fmt.Sprintf("%s_%s_%s_next_open_price", account.Apikey, strategy.TradingDirection, strategy.TradingCurrency)
	// 计算止盈价格
	tpPrice := entity.CalculateTargetPrice(strategy.TradingDirection, price, strategy.TpProfitRatio)
	// 构建创建订单请求
	createOrderRequest := &pkgokx.CreateOrderRequest{
		InstID:      strategy.TradingCurrency,
		TdMode:      entity.TdModeIsolated,
		OrdType:     ordType,
		Px:          price,
		Sz:          fmt.Sprintf("%f", strategy.OrderAmount),
		TpTriggerPx: tpPrice,
		TpOrdPx:     tpPrice,
		ClOrdId:     generate.GenerateUniqueCode(),
	}
	createOrderRequest.Side, createOrderRequest.PosSide = entity.GetSidePosSide(strategy.TradingDirection)
	// 创建订单
	order, err := okx.CreateOrder(createOrderRequest)
	if err != nil {
		log.Println(err)
		return err
	}
	// 计算补仓价格
	nextOpenPrice := entity.CalculateCoverPositionPrice(strategy.TradingDirection, price, strategy.CoverPositionRatio)
	// 止盈价格
	tPkey := fmt.Sprintf("%s_%s_%s_next_tp_price", account.Apikey, strategy.TradingDirection, strategy.TradingCurrency)
	// 保存止盈价格
	//err = redisClient.Set(tPkey, tpPrice, -1)
	err = redisClient.Client.LPush(ctx, tPkey, tpPrice).Err()
	if err != nil {
		log.Println(err)
		return err
	}
	log.Printf("%s 补仓价格: %s\n", account.Name, nextOpenPrice)
	// 保存下次开仓价格
	err = redisClient.Set(key, nextOpenPrice, -1)
	if err != nil {
		log.Println(err)
		return err
	}
	// 可以根据需要处理订单信息
	log.Printf("%s Order created successfully: %+v\n", strategy.TradingCurrency, order)
	return nil
}

// initLog 初始化日志系统，将日志输出重定向到指定的文件。
func initLog(filePath string) {
	logFile, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Fatalf("error opening file: %v", err)
	}
	// 这里打开的日志文件应该在程序的整个生命周期内保持打开状态，直到程序准备退出时手动关闭。
	log.SetOutput(logFile)
	// 可选：设置日志格式
	log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds | log.Llongfile)
}

func main() {
	// 初始化日志
	initLog("./go_okx_bot.log")
	// 加载配置
	conf, err := config.ReadConfig("./config.yml")
	if err != nil {
		panic(err)
		return
	}
	// 创建信号的channel
	sigChan := make(chan os.Signal, 1)
	// 通知signal包应将传入的Interrupt信号转发到sigChan
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
	//  创建redis客户端
	redisClient := redisclient.NewRedisClient(conf.Redis.Addr, conf.Redis.Password, conf.Redis.DB)
	// 配置监听
	viper.WatchConfig()
	viper.OnConfigChange(func(e fsnotify.Event) {
		log.Println("Config file changed")
		mutex.Lock()
		if err := viper.Unmarshal(&conf); err != nil {
			log.Println("Error unmarshalling config:", err)
		}
		mutex.Unlock()
	})
	// 实盘执行
	for _, account := range conf.Accounts {
		log.Printf("实盘开始执行: %s\n", account.Name)
		// 创建策略处理
		go strategyHandle(ctx, redisClient, account)
	}

	// 初始化web
	r := gin.Default()
	// 处理根路径的请求
	r.GET("/", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "欢迎来到Gin Web服务",
		})
	})
	// 处理 /start/账户 路径的请求
	r.GET("/start/:account", func(c *gin.Context) {
		// 从URL路径中获取账户名
		account := c.Param("account")
		key := c.DefaultQuery("key", "")
		// 如果没有提供账户名，处理全部账户
		for _, accountConfig := range conf.Accounts {
			if account != "" && account != accountConfig.Name {
				continue
			}
			for _, strategy := range accountConfig.StrategyList {
				if key != "" && key != strategy.Key {
					continue
				}
				apiKey := accountConfig.Apikey
				secretKey := accountConfig.Secret
				passphrase := accountConfig.Password
				okx, err := pkgokx.NewOkx(ctx, apiKey, secretKey, passphrase)
				if err != nil {
					return
				}
				price, err := okx.GetTickerLastPrice(strategy.TradingCurrency)
				if err != nil {
					log.Println(err)
					return
				}
				err = CreateOrder(okx, redisClient, price, entity.OrdTypeMarket, accountConfig, strategy)
				if err != nil {
					log.Printf("Create order failed: %s\n", err)
					return
				}
			}
		}
		c.JSON(200, gin.H{
			"message": "策略开始执行",
		})
	})

	// 处理 /stop/账户 路径的请求
	r.GET("/stop/:account", func(c *gin.Context) {
		// 从URL路径中获取账户名
		account := c.Param("account")
		key := c.DefaultQuery("key", "")
		// 如果没有提供账户名，处理全部账户
		for _, accountConfig := range conf.Accounts {
			if account != "" && account != accountConfig.Name {
				continue
			}
			for _, strategy := range accountConfig.StrategyList {
				if key != "" && key != strategy.Key {
					continue
				}
				apiKey := accountConfig.Apikey
				// 删除key
				delKey := fmt.Sprintf("%s_%s_%s_next_open_price", apiKey, strategy.TradingDirection, strategy.TradingCurrency)
				err = redisClient.Del(delKey)
				log.Printf("stop key: %s\n", delKey)
				if err != nil {
					log.Printf("delete key failed: %v", err)
					return
				}

			}
		}
		c.JSON(200, gin.H{
			"message": "策略已停止",
		})
	})

	go func() {
		// 启动HTTP服务
		err = r.Run(":8090")
		if err != nil {
			fmt.Println("start http server failed: ", err)
			return
		}
	}()

	// 定时清理监听价格的key 30s清理一次
	go func() {
		for {
			for _, account := range conf.Accounts {
				for _, strategy := range account.StrategyList {
					key := fmt.Sprintf("%s_%s_%s_next_open_price", account.Apikey, strategy.TradingDirection, strategy.TradingCurrency)
					_, err := redisClient.Get(key)
					if err == nil {
						// 查下是否存在仓位 如果存在仓位则不清理
						apiKey := account.Apikey
						secretKey := account.Secret
						passphrase := account.Password
						okx, err := pkgokx.NewOkx(ctx, apiKey, secretKey, passphrase)
						if err != nil {
							return
						}
						positions, err := okx.GetPositions(strategy.TradingCurrency, strategy.TradingDirection)
						if err != nil {
							return
						}
						if positions == 0 {
							// 清理key
							log.Printf("清理key: %s\n", key)
							err = redisClient.Del(key)
							if err != nil {
								log.Println(err)
								break
							}
						}

					}
				}
			}
			time.Sleep(30 * time.Second)
		}
	}()

	// 阻塞，直到从sigChan接收到信号
	<-sigChan
	// 退出程序
	log.Println("马丁策略机器人程序关闭。")
}

func strategyHandle(ctx context.Context, redisClient *redisclient.RedisClient, accountConfig *config.AccountConfig) {
	apiKey := accountConfig.Apikey
	secretKey := accountConfig.Secret
	passphrase := accountConfig.Password
	okx, err := pkgokx.NewOkx(ctx, apiKey, secretKey, passphrase)
	if err != nil {
		return
	}
	strategyList := accountConfig.StrategyList

	// 循环调度
	for {
		for _, strategy := range strategyList {
			// Redis key
			key := fmt.Sprintf("%s_%s_%s_next_open_price", apiKey, strategy.TradingDirection, strategy.TradingCurrency)

			// 获取最新价格
			price, err := okx.GetTickerLastPrice(strategy.TradingCurrency)
			if err != nil {
				log.Println(err)
				return
			}
			// 止盈价格
			tPkey := fmt.Sprintf("%s_%s_%s_next_tp_price", apiKey, strategy.TradingDirection, strategy.TradingCurrency)
			count := redisClient.Client.LLen(ctx, tPkey)
			if count.Val() > 0 {
				// 获取第一个价格
				tpPrice, err := redisClient.Client.LIndex(ctx, tPkey, 0).Result()
				if err != nil {
					log.Println(err)
					continue
				}
				// 获取止盈价格
				tpResult, err := decimal.CompareFloatStrings(price, tpPrice)
				if err != nil {
					log.Println(err)
					continue
				}
				longTp := strategy.TradingDirection == entity.LONG && tpResult == 1
				shortTp := strategy.TradingDirection == entity.SHORT && tpResult == -1
				if longTp || shortTp {
					// 更新下次开仓价格
					openPrice := entity.CalculateCoverPositionPrice(strategy.TradingDirection, price, strategy.CoverPositionRatio)
					err = redisClient.Set(key, openPrice, -1)
					if err != nil {
						log.Println(err)
						continue
					}
					log.Printf("%s %s 止盈后下次开仓价格: %s\n", accountConfig.Name, strategy.TradingCurrency, openPrice)
					// 清理key
					err = redisClient.Client.LPop(ctx, tPkey).Err()
					if err != nil {
						log.Println(err)
						return
					}
					continue
				}
			}

			// 获取redis中的下次开仓价格
			nextOpenPrice, err := redisClient.Get(key)
			if err == nil {
				if price == "" || price == "0" {
					continue
				}
				result, err := decimal.CompareFloatStrings(price, nextOpenPrice)
				if err != nil {
					log.Println(err)
					continue
				}
				// 判断是否需要开仓
				// 如果是做多策略，且当前价格小于下次开仓价格，则开仓
				open1 := strategy.TradingDirection == entity.LONG && result == -1
				// 如果是做空策略，且当前价格大于下次开仓价格，则开仓
				open2 := strategy.TradingDirection == entity.SHORT && result == 1

				if open1 || open2 {
					// 创建订单
					err = CreateOrder(okx, redisClient, price, entity.OrdTypeLimit, accountConfig, strategy)
					if err != nil {
						log.Printf("Create order failed: %s\n", err)
						continue
					}
				}
			}
		}
		time.Sleep(500 * time.Millisecond)
	}
}
